home *** CD-ROM | disk | FTP | other *** search
- /***************************************************************************
- * *
- * SktSocketManager.m *
- * Copyright 1992 by Nik A Gervae *
- * *
- * One of a set of three Objective-C classes (SktSocketManager, SktSocket, *
- * and SktSocketUser) which implement a convenient interface to Berkeley *
- * stream sockets under NeXTSTEP(r). See the accompanying class *
- * specifications (files with a .rtf or .spec suffix) for further *
- * information. *
- * *
- * NeXTSTEP is a registered trademark of NeXT Computer, Inc. *
- * *
- ****************************************************************************
- * *
- * LICENSE *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation. *
- * *
- * The program and this makefile are distributed in the hope that it will *
- * be useful, but are provided "AS IS" AND WITHOUT ANY WARRANTY; without *
- * any express or implied warranty of MERCHANTABILITY or FITNESS FOR A *
- * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
- * Any use or distribution of the program and documentation must include *
- * appropriate copyrights to acknowledge Nik A. Gervae and the Free *
- * Software Foundation, Inc. *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the Free Software *
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- ****************************************************************************
- * *
- * VERSION HISTORY *
- * *
- * Version numbers are simply dates in the form YYYYMMDD. These represent *
- * the date that version was finished. Only significantly changed versions *
- * are reported here, or those versions requiring explanation of changes. *
- * There may be many interim stages between dated versions. *
- * *
- * DateVersion Primary Author Notes *
- * ----------- --------------- -------------------------------------------- *
- * 19920327 Nik A Gervae First released version *
- * 19920723 Nik A Gervae Actually released *
- * *
- ***************************************************************************/
-
- #import <errno.h>
- #import <stdio.h>
-
- #import <sys/param.h>
- #import <sys/types.h>
- #import <sys/socket.h>
- #import <sys/time.h>
- #import <netinet/in.h>
- #import <netdb.h>
-
- #import <objc/List.h>
-
- #import "SktSocketManager.h"
-
- extern int errno;
-
-
- /***************************************************************************
- * *
- * These are the constant strings used. Feel free to translate them into *
- * your favorite language. Do be sure to keep all the % directives in *
- * place, or change the code that accesses these strings. *
- * *
- ***************************************************************************/
- #define STR_Error "ERROR (%s): "
-
- #define STR_MallocFatalError STR_Error "zone malloc failed.\n"
-
- #define STR_BadCapacity STR_Error "specified capacity (%d) invalid.\n"
- #define STR_NoUserClass STR_Error "no user class specified for " \
- "initializing.\n"
- #define STR_NotUserClass STR_Error "%s is not a subclass of " \
- "SktSocketUser.\n"
- #define STR_CantGetSocket STR_Error "unable to get service socket.\n"
- #define STR_CantSetOptions STR_Error "unable to set sender\'s options.\n"
- #define STR_CantBindSocket STR_Error "unable to bind socket.\n"
- #define STR_CantGetSocketName STR_Error "unable to get socket name.\n"
- #define STR_CantCreateList STR_Error "unable to create openSockets " \
- "list.\n"
-
-
- #define STR_NoHostInfo "(%s) Cannot get host information.\n"
- #define STR_NoFds "(%s) No more fds. Closing new connection.\n"
- #define STR_NoNewConnects "No new connections can be accepted at this " \
- "time. Sorry.\n"
- #define STR_ClosingSocket "(%s) closing SktSocket with socketFd %d\n"
-
- #define STR_SelectEBADF "(%s) bad fd in select().\n"
- #define STR_SelectEINTR "(%s) signal during select().\n"
- #define STR_SelectEINVAL "(%s) invalid timeout in select().\n"
- #define STR_SelectErrUnknown "(%s) unknown select() error #%d.\n"
- #define STR_NewSocketDied STR_Error "coudn\'t create/init new socket.\n"
- #define STR_NewUserDied STR_Error "coudn\'t create/init new user.\n"
- #define STR_CantCreateUserSorry "Can\'t create needed resources to accept " \
- "a new connection. Sorry.\n"
-
- #define STR_Unknown "(unknown)"
-
-
-
- @implementation SktSocketManager
-
- /***************************************************************************
- * *
- * -init *
- * *
- ***************************************************************************/
- - init
- {
- return [self notImplemented:_cmd];
- }
-
- /***************************************************************************
- * *
- * -initPort:logfile:fdCapacity:userClass:options: *
- * *
- * THIS IS THE DESIGNATED INITIALIZER FOR THIS CLASS. *
- * *
- * This method does a hell of a lot. It has to establish all ivars, check *
- * the amount of available file descriptors for sockets, check the supplied *
- * user class, get a socket for service connections, bind it, verify is, *
- * and listen for connections. See below for the nitty-gritty. *
- * *
- * ERROR CONDITIONS: The following conditions cause a return of nil: *
- * *
- * - The specified capacity is invalid. *
- * - The specified user class is nil or is not a subclass of SktSocketUser. *
- * - The socket can't be created or bound, or verified. *
- * - setSocketOptions returns nil. *
- * - The List object of open SktSockets can't be allocated/initted. *
- * *
- ***************************************************************************/
- - initPort:(int)portNum logfile:(FILE *)file fdCapacity:(int)cap
- userClass:aClass
- {
- int slen = sizeof(struct sockaddr_in); // for use in inet functions
- char host_name[MAXHOSTNAMELEN+1]; // for getting the hostname
- struct sockaddr_in server; // for binding the socket
- struct sockaddr_in hostaddr_in; // for getting the inet address
- struct hostent *host; // for getting the inet address
-
- [super init];
- zone = [self zone];
-
- /*
- * Set up the logfile right away, and initialize ivars to be void
- * until we can fill them in later. Also check cap against the
- * number of available fds.
- */
- logfile = file;
- userClass = nil;
- openSockets = nil;
- hostaddress = hostname = NULL;
- doesLog = YES;
-
- /*
- * Default select() behavior in -update is to poll (0 timeout),
- * not block, and not to cover any signals.
- */
- selectTimeout.tv_sec = 0;
- selectTimeout.tv_usec = 0;
- timeoutIndefinite = NO;
- selectSignalMask = 0;
-
- if (0 > cap || getdtablesize() < cap) {
- [self log:STR_BadCapacity, [self name], cap];
- return [self free];
- }
- numAvailFds = cap;
-
- /*
- * If aClass is not a subclass of SktSocketUser, don't init.
- */
- if (!aClass) {
- [self log:STR_NoUserClass, [self name]];
- return [self free];
- }
-
- if (![aClass isKindOf:objc_getMetaClass([[SktSocketUser class] name])]) {
- [self log:STR_NotUserClass, [self name], [aClass name]];
- return [self free];
- }
-
- userClass = [aClass class]; // Send class message just in case.
-
- /*
- * A new socket, please. And don't init if we can't get one.
- * If the sender wanted to set some options, try to do so, but
- * don't init if the provided function returns NO.
- */
- serviceSocketFd = socket(AF_INET, SOCK_STREAM, 0);
- if (0 > serviceSocketFd) {
- [self log:STR_CantGetSocket, [self name]];
- return [self free];
- }
-
- if (![self setSocketOptions:serviceSocketFd]) {
- [self log:STR_CantSetOptions, [self name]];
- return [self free];
- }
-
- /*
- * bind() the socket. This part is messy, 'cause we have to get
- * all sorts of random garbage info to get what we really need.
- */
- gethostname(host_name, sizeof(host_name));
- if (NULL == (host = gethostbyname(host_name))) {
- server.sin_family = AF_INET;
- hostaddress = NULL;
- }
- else {
- server.sin_family = host->h_addrtype;
- memcpy((char *)&hostaddr_in.sin_addr.s_addr, (char *)host->h_addr_list[0],
- host->h_length);
-
- /*
- * Not fatal if this fails. (May need to change this.)
- */
- hostaddress = zoneStrdup([self zone],
- (char *)inet_ntoa(hostaddr_in.sin_addr));
-
- if (!hostaddress) {
- [self log:STR_MallocFatalError, [self name]];
- }
- }
-
- /*
- * Not fatal if this fails. (May need to change this.)
- */
- hostname = zoneStrdup([self zone], host_name);
- if (!hostname) {
- [self log:STR_MallocFatalError, [self name]];
- }
-
- server.sin_addr.s_addr = htonl(INADDR_ANY);
- server.sin_port = htonl(portNum);
- if (bind(serviceSocketFd, (struct sockaddr *)&server, slen)) {
- [self log:STR_CantBindSocket, [self name]];
- return [self free];
- }
-
- /*
- * Verify socket and listen() for connection requests.
- */
- if (0 > getsockname(serviceSocketFd, (struct sockaddr *)&server, &slen)) {
- [self log:STR_CantGetSocketName, [self name]];
- return [self free];
- }
- listen(serviceSocketFd, 5);
-
- /*
- * Establish more instance variables.
- */
- servicePort = portNum;
- maxSocketFd = serviceSocketFd;
-
- openSockets = [[List allocFromZone:[self zone]] initCount:5];
-
- if (!openSockets) {
- [self log:STR_CantCreateList, [self name]];
- return [self free];
- }
-
- return self;
-
- } /*initPort:logfile:fdCapacity:userClass:*/
-
- /***************************************************************************
- * *
- * -setSocketOptions *
- * *
- * This method sets no options, as the SktSocketManager needs none set. It *
- * may be overriden in subclasses to set other socket/file descriptor *
- * options via setsockopt/fcntl. *
- * *
- * If this method returns nil, then any initialization is aborted and the *
- * init... method will return nil. *
- * *
- ***************************************************************************/
- - setSocketOptions:(int)fd
- {
- return self;
- }
-
- /***************************************************************************
- * *
- * -free *
- * *
- * Does the obvious. Also kills all connections. *
- * *
- ***************************************************************************/
- - free
- {
- if (hostaddress) free(hostaddress);
- if (hostname) free(hostname);
- [self closeAllSockets];
- if (openSockets) [openSockets free];
- shutdown(serviceSocketFd, 2);
- return [super free];
- }
-
- /***************************************************************************
- * *
- * -setDoesLog: *
- * *
- * Allows you to change whether the user logs non-error messages (which may *
- * nonetheless be quite important). The default is YES. *
- * *
- ***************************************************************************/
- - setDoesLog:(BOOL)flag
- {
- doesLog = flag;
- return self;
- }
-
- /***************************************************************************
- * *
- * -doesLog *
- * *
- * YES if non-error messages are logged (the default), NO if not. Simple. *
- * *
- ***************************************************************************/
- - (BOOL)doesLog
- {
- return doesLog;
- }
-
- /***************************************************************************
- * *
- * -setTimeoutSeconds:microseconds: *
- * *
- * Sets the timeout for checking i/o. The SktSocketManager will not wait *
- * longer than this before beginning to process an -update message. If the *
- * timeout is (0,0) then -update simply polls for immediately available i/o *
- * operations and performs them. *
- * *
- * This method clears and deactivates timeoutIndefinite. *
- * *
- ***************************************************************************/
- - setTimeoutSeconds:(long int)secs microseconds:(long int)usecs
- {
- if (0 > secs || 0 > usecs) return nil;
-
- timeoutIndefinite = NO;
- selectTimeout.tv_sec = secs;
- selectTimeout.tv_usec = usecs;
-
- return self;
- }
-
- /***************************************************************************
- * *
- * -getTimeoutSeconds:microseconds: *
- * *
- * Gets the timeout for checking i/o. The SktSocketManager will not wait *
- * longer than this before beginning to process an -update message. If the *
- * timeout is (0,0) then -update simply polls for immediately available i/o *
- * operations and performs them. *
- * *
- * This method returns NO if the timeout does not apply; that is, if *
- * timeoutIndefinite is in effect. *
- * *
- ***************************************************************************/
- - (BOOL)getTimeoutSeconds:(long int *)secs microseconds:(long int *)usecs
- {
- *secs = selectTimeout.tv_sec;
- *usecs = selectTimeout.tv_usec;
-
- return !timeoutIndefinite;
- }
-
- /***************************************************************************
- * *
- * -setTimeoutIndefinite *
- * *
- * Causes the SktSocketManager to block indefinitely in -update until there *
- * is an i/o operation to perform, or a signal occurs. *
- * *
- ***************************************************************************/
- - setTimeoutIndefinite
- {
- timeoutIndefinite = YES;
- return self;
- }
-
- /***************************************************************************
- * *
- * -isTimeoutIndefinite *
- * *
- * Returns whether the SktSocketManager blocks indefinitely in -update *
- * until there is an i/o operation to perform, or a signal occurs. *
- * *
- ***************************************************************************/
- - (BOOL)isTimeoutIndefinite
- {
- return timeoutIndefinite;
- }
-
- /***************************************************************************
- * *
- * -setSignalMask: *
- * *
- * Sets the signal mask used during a select(), and returns the old one. *
- * Note the difference from the standard sigsetmask() system call! *
- * *
- ***************************************************************************/
- - (int)setSignalMask:(int)sigmask;
- {
- int oldmask = selectSignalMask;
- selectSignalMask = sigmask;
- return oldmask;
- }
-
- /***************************************************************************
- * *
- * -signalMask *
- * *
- * Returns the signal mask used during a select(). This is not a cover for *
- * the sigmask() system call! *
- * *
- ***************************************************************************/
- - (int)signalMask;
- {
- return selectSignalMask;
- }
-
- /***************************************************************************
- * *
- * -setFdCapacity: *
- * *
- * Sets the maximum amount of connections that will be allowed at one time. *
- * Various error checking is done. This method can be used to reserve some *
- * of the process's file descriptors for other use (log/temp files). *
- * *
- ***************************************************************************/
- - setFdCapacity:(int)cap
- {
- int assigned;
-
- assigned = [openSockets count];
-
- if (getdtablesize() < cap || assigned > cap) {
- return nil;
- }
- else if (0 > cap) {
- numAvailFds = 0;
- return nil;
- }
- else {
- numAvailFds = cap - assigned;
- }
- return self;
- }
-
- /***************************************************************************
- * *
- * -adjustFdCapacity: *
- * *
- * Adjusts the maximum amount of connections that will be allowed at one *
- * time. Various error checking is done. This method can be used to *
- * reserve some of the process's file descriptors for other use (log/temp *
- * files). *
- * *
- ***************************************************************************/
- - (int)adjustFdCapacity:(int)byAmount
- {
- int newAvailFds;
- int assigned;
-
- newAvailFds = numAvailFds + byAmount;
- assigned = [openSockets count];
-
- if (getdtablesize() < newAvailFds + assigned) {
-
- return -1;
-
- } else if (0 > newAvailFds) {
-
- numAvailFds = 0;
- return -1;
-
- }
- else {
- numAvailFds = newAvailFds;
- }
- return numAvailFds + assigned;
- }
-
- /***************************************************************************
- * *
- * -fdCapacity *
- * *
- * Returns the maximum amount of connections that will be allowed at one *
- * time. *
- * *
- ***************************************************************************/
- - (int)fdCapacity
- {
- return numAvailFds + [openSockets count];
- }
-
- /***************************************************************************
- * *
- * -numAvailFds *
- * *
- * Returns the amount of file desctiptors free for further connections. *
- * *
- ***************************************************************************/
- - (int)numAvailFds
- {
- return numAvailFds;
- }
-
- /***************************************************************************
- * *
- * -logfile *
- * *
- * Return the FILE pointer for the file all log messages are written to. *
- * *
- ***************************************************************************/
- - (FILE *)logfile
- {
- return logfile;
- }
-
- /***************************************************************************
- * *
- * -hostaddress *
- * *
- * Returns the address of the host the SktSocketManager's process is *
- * on, in dot notation. *
- * *
- ***************************************************************************/
- - (const char *)hostaddress
- {
- if (hostaddress) return (const char *)hostaddress;
- else return STR_Unknown;
- }
-
- /***************************************************************************
- * *
- * -hostname *
- * *
- * Returns the name of the host the SktSocketManager's process is on. *
- * *
- ***************************************************************************/
- - (const char *)hostname
- {
- if (hostname) return (const char *)hostname;
- else return STR_Unknown;
- }
-
- /***************************************************************************
- * *
- * -servicePort *
- * *
- * The Internet port number used to connect to the SktSocketManager. *
- * *
- ***************************************************************************/
- - (int)servicePort
- {
- return servicePort;
- }
-
- /***************************************************************************
- * *
- * -getInetAddresses *
- * *
- * Return a NULL-terminated array of character strings representing all of *
- * the Internet addresses of the host machine. The sender may free the *
- * array returned when it is no longer needed (just free the top-level *
- * pointer). *
- * *
- * The array is allocated in the default malloc zone. *
- * *
- * ERROR CONDITION: If the allocation of the array fails, a message is *
- * logged, and NULL is returned. *
- * *
- ***************************************************************************/
- - (char **)getInetAddresses
- {
- struct hostent *host; // the record for the host
- struct sockaddr_in thisHostaddr; //
- int count, this;
- int totalSize;
- char **addresses; // Used to return inet addresses
- char *loc;
-
- /*
- * Get the host's name and count the number of addresses it has.
- */
- if (NULL == (host = gethostbyname(hostname))) {
- [self log:STR_NoHostInfo, [self name]];
- return NULL;
- }
- for (count = 0; host->h_addr_list[count] != NULL; count++)
- ;
-
- /*
- * Count up the total amount we need to malloc to get all the
- * strings and their pointers.
- */
- totalSize = (1+count) * sizeof(char *);
-
- for (this = 0; this < count && NULL != host->h_addr_list[this]; this++) {
- memcpy((char *)&(thisHostaddr.sin_addr.s_addr),
- (char *)host->h_addr_list[this],
- host->h_length);
-
- totalSize += 1 + strlen(inet_ntoa(thisHostaddr.sin_addr));
- }
- addresses = (char **)NXZoneMalloc(NXDefaultMallocZone(), totalSize);
- if (!addresses) {
- [self log:STR_MallocFatalError, [self name]];
- return NULL;
- }
-
- /*
- * Now copy each address into the proper offset in the array, and
- * set a pointer to it at the beginning.
- */
- loc = (char *)(addresses + count + 1);
- for (this = 0; this < count && NULL != host->h_addr_list[this]; this++) {
-
- char *dummy;
- long int size;
-
- memcpy((char *)&(thisHostaddr.sin_addr.s_addr),
- (char *)host->h_addr_list[this],
- host->h_length);
- dummy = (char *)inet_ntoa(thisHostaddr.sin_addr);
- size = 1+strlen(dummy);
-
- *(addresses + this) = loc;
- memcpy(loc, dummy, size);
- loc += size;
- }
-
- /*
- * Remember, it's a NULL-terminated array of arrays.
- */
- addresses[this] = NULL;
-
- return addresses;
-
- } /*getInetAddresses*/
-
- /***************************************************************************
- * *
- * -update *
- * *
- * In brief (see below for details): *
- * - Do asynchronous input via select(). This is the messy part. *
- * - Drop any sockets with exceptions pending. *
- * - Have individual sockets flush output. *
- * - Have them queue input. *
- * - Check for new connections. (Always do this last.) *
- * *
- * ERROR CONDITION: If a new SktSocket/User pair need to be created, and *
- * either can't be, this method logs a message and returns immediately. *
- * This is not a fatal error. *
- * *
- ***************************************************************************/
- - update
- {
- int i; // general loop variable
- id currentSocket; // use in loops
- int currentSocketFd; // use in loops
-
- int oldmask; // used to protect select() from signals
- int numpending; // result of select()
- fd_set readfds; // for select()
- fd_set exceptfds; // for select()
- struct timeval *timeout; // for select()
-
- /*
- * Prepare for select(). Set the timeout, the fd sets, and
- * mask any signals requested.
- */
- if (timeoutIndefinite) timeout = NULL;
- else timeout = &selectTimeout;
-
- FD_ZERO(&readfds);
- FD_ZERO(&exceptfds);
-
- FD_SET(serviceSocketFd, &readfds);
- for (i = [openSockets count]-1; 0 <= i; i--) {
- currentSocketFd = [[openSockets objectAt:i] socketFd];
- FD_SET(currentSocketFd, &readfds);
- FD_SET(currentSocketFd, &exceptfds);
- }
-
- oldmask = sigsetmask(selectSignalMask);
-
- numpending = select(maxSocketFd+1,
- &readfds,
- (fd_set *)0, // SktSockets handle write()s
- &exceptfds,
- timeout);
- sigsetmask(oldmask);
-
- /*
- * Error occured: report and don't do anything this time
- * (odds are it was a signal).
- */
- if (0 > numpending) {
- if (doesLog) switch (errno) {
- case EBADF:
- [self log:STR_SelectEBADF, [self name]];
- break;
- case EINTR:
- [self log:STR_SelectEINTR, [self name]];
- break;
- case EINVAL:
- [self log:STR_SelectEINVAL, [self name]];
- break;
- default:
- [self log:STR_SelectErrUnknown, [self name], errno];
- break;
- }
- return self;
- }
-
- /*
- * Drop sockets with exception conditions (assume corrupt connection).
- * It is the socket user's responsibility to notice that its SktSocket
- * is gone, and to clean up appropriately.
-
- * Have to go backwards 'cause List slides contents forward on removal.
- */
- for (i = [openSockets count]-1; 0 <= i; i--) {
-
- currentSocket = [openSockets objectAt:i];
- currentSocketFd = (int)[currentSocket socketFd];
-
- if (FD_ISSET(currentSocketFd, &exceptfds)) {
- FD_CLR(currentSocketFd, &readfds);
- [self closeSocket:currentSocket];
- }
-
- }
-
- /*
- * Have all sockets flush output. We don't need writefds for this.
- */
- [openSockets makeObjectsPerform:@selector(flushOutput)];
-
- /*
- * Have each socket read one block of input. Use the info from
- * select() in this messy loop to avoid the hassle and overhead
- * of nonblocking read()'s where they might be unnecessary.
- * If readInput returns nil, the socket connection is closed/dead
- * (EOF), and the SktSocket should be closed. It is the socket
- * user's responsibility to notice that its SktSocket is gone,
- * and to clean up appropriately.
- */
- for (i = [openSockets count]-1; 0 <= i; i--) {
-
- currentSocket = [openSockets objectAt:i];
- currentSocketFd = [currentSocket socketFd];
-
- if (FD_ISSET(currentSocketFd, &readfds)) {
- if (![currentSocket readInput]) {
- [self closeSocket:currentSocket];
- }
- }
- }
-
- /*
- * Accept new connections if possible.
- */
- if (FD_ISSET(serviceSocketFd, &readfds)) {
-
- id newSocket;
- int newSocketFd;
- id newUser;
-
- /*
- * Create a new SktSocket and a user. Log messages if they
- * can't be created, and return.
- */
- newSocket = [[SktSocket allocFromZone:zone] initOnFd:serviceSocketFd
- withManager:self];
- if (!newSocket) {
- [self log:STR_NewSocketDied, [self name]];
- return self;
- }
-
- newSocketFd = [newSocket socketFd];
-
- newUser = [[userClass allocFromZone:zone] initWithSocket:newSocket];
- if (!newUser) {
- [self log:STR_NewUserDied, [self name]];
- [newSocket queueOutputString:STR_CantCreateUserSorry];
- [newSocket flushOutput];
- [newSocket free];
- return self;
- }
-
- /*
- * Check that we have room for it and update bookkeeping info.
- */
- if ([openSockets count] < numAvailFds) {
- if (newSocketFd > maxSocketFd) maxSocketFd = newSocketFd;
- [openSockets addObject:newSocket];
- }
- else {
- [self log:STR_NoFds, [self name]];
- [newSocket queueOutputString:STR_NoNewConnects];
- [newSocket flushOutput];
-
- [newSocket free];
- [newUser free];
- }
-
- } /*if (FD_ISSET())*/
-
- return self;
-
- } /*update*/
-
- /***************************************************************************
- * *
- * -closeSocket: *
- * *
- * Remove a socket from the list of currently open sockets, have the *
- * SktSocket object clean itself up and die, and update bookeeping info. *
- * Do NOT kill the socket user here! It has to find out its socket is gone *
- * and take appropriate action. *
- * *
- ***************************************************************************/
- - closeSocket:socketObj
- {
- [self log:STR_ClosingSocket, [self name], [socketObj socketFd]];
-
- [openSockets removeObject:socketObj];
- if ([socketObj socketFd] == maxSocketFd) {
- maxSocketFd--;
- }
-
- [socketObj free];
-
- return self;
-
- }
-
- /***************************************************************************
- * *
- * closeAllSockets *
- * *
- * Have all SktSocket objects clean up and die. *
- * *
- ***************************************************************************/
- - closeAllSockets
- {
- int i;
-
- for (i = [openSockets count] - 1; 0 <= i; i--) {
- [self closeSocket:[openSockets objectAt:i]];
- }
- return self;
-
- }
-
- /***************************************************************************
- * *
- * -announceString: *
- * *
- * Dump the given string on each socket. *
- * *
- ***************************************************************************/
- - announceString:(const char *)announcement
- {
- int numfds;
- int i;
-
- numfds = [openSockets count];
- for (i = 0; i < numfds; i++) {
- [[openSockets objectAt:i] queueOutputString:announcement];
- }
-
- return self;
- }
-
- /***************************************************************************
- * *
- * -log: *
- * *
- * Print a message to the logfile. You can use this exactly like printf, *
- * 'cause that's what it uses. *
- * *
- ***************************************************************************/
- - log:(const char *)format, ...
- {
- va_list param_list;
-
- va_start(param_list, format);
- vfprintf(logfile, format, param_list);
- fflush(logfile);
- va_end(param_list);
- return self;
-
- }
-
- @end /*implementation SktSocketManager*/
-